/*
 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "aos/kernel.h"
#include "aos/yloop.h"
#include "ulog/ulog.h"

#include <atcmd_config_module.h>

#include <atparser.h>
#include <hal_sal.h>

#define TAG "n720_lte_module"

#define AT_UART_BAUDRATE                    115200

#define AT_CMD_TCP_CONNECT              "AT+TCPSETUP"
#define AT_CMD_TCP_CLOSE                "AT+TCPCLOSE"
#define AT_CMD_TCP_SEND                 "AT+TCPSEND"
#define AT_CMD_TCP_PREFIX               "+TCP"
#define AT_CMD_TCP_SEND_PROMPT          "\r\n>"

#define AT_CMD_PDP_SET                  "AT+CGDCONT=%d,%s,%s\r"
#define AT_CMD_PDP_AUTH                 "AT+XGAUTH=%d,%d,%s,%s\r"
#define AT_CMD_PDP_CONNECT              "AT+XIIC=1\r"
#define AT_CMD_PDP_IP_QUERY             "AT+XIIC?\r"

#define AT_CMD_IMEI_GET                 "AT+CGSN\r"
#define AT_CMD_VERSION_GET              "AT+GMR\r"
#define AT_CMD_SIM_PIN_CHECK            "AT+CPIN?\r"
#define AT_CMD_CIMI_GET                 "AT+CIMI"
#define AT_CMD_SIGNAL_QUALITY_CHECK     "AT+CSQ\r"
#define AT_CMD_NETWORK_REG_CHECK        "AT+CREG?\r"
#define AT_CMD_ATTACH_CHECK             "AT+CGATT?\r"
#define AT_CMD_ECHO_OFF                 "ATE0\r"
#define AT_CMD_AT                       "AT\r"

#define AT_CMD_NET_CMD_PREFIX           "+C"
#define AT_CMD_XIIC_PREFIX              "+XIIC:    "

#define AT_CMD_PREFIX_MAX_LEN          32
#define AT_CMD_RETRY_MAX               20

/* Max LinkNum in n720 module */
#define N720_MAX_LINK_NUM               5
#define AT_WAIT_DEF_TIMEOUT             3000
#define N720_MAX_AT_CMD_LENGTH          256

/* Change to include data slink for each link id respectively. <TODO> */
typedef struct link_s {
    int fd;
    aos_sem_t sem_start;
    aos_sem_t sem_close;
} link_t;

typedef enum {
   WAIT_NONE = 0,
   WAIT_SIMPLE_RSP,    /* OK or Error */
   WAIT_CGSN_RSP,
   WAIT_PIN_RSP,
   WAIT_REG_RSP,
   WAIT_SIGQ_RSP,
   WAIT_ATTCH_RSP,
   WAIT_IP_QUERY_RSP,
   WAIT_CONN_RSP,
   WAIT_DISC_RSP,
   WAIT_SEND_PMT,
   WAIT_SEND_RSP,
} at_module_send_state_t;

typedef enum {
   RSP_ERROR  = -1,
   RSP_OK     = 0,
   RSP_PROMPT = 1,
} at_rsp_val_t;

typedef enum {
    AT_RECV_POLLING = 0,
    AT_RECV_AUTO    = 1,
} at_recvmode_e;

static uint8_t inited = 0;
static link_t g_link[N720_MAX_LINK_NUM];
static aos_mutex_t g_link_mutex;
static aos_sem_t   g_st_at_sem;
static volatile at_module_send_state_t  atcmd_send_state = WAIT_NONE;
static volatile at_rsp_val_t            rsp_val = RSP_ERROR;

static netconn_data_input_cb_t g_netconn_data_input_cb;

static uart_dev_t uart_dev;
int at_dev_fd = -1;


static int atcmd_wait_notify(int timeout_ms)
{
    return aos_sem_wait(&g_st_at_sem, timeout_ms);
}

static void atcmd_notify_sender(void)
{
    aos_sem_signal(&g_st_at_sem);
}

static void atcmd_set_state(at_module_send_state_t state)
{
    atcmd_send_state = state;
}

static at_module_send_state_t atcmd_get_state(void)
{
    return atcmd_send_state;
}

static bool atcmd_send_state_ready(void)
{
    return (atcmd_send_state == WAIT_NONE);
}

static at_rsp_val_t atcmd_get_ret_val(void)
{
    return rsp_val;
}

static void atcmd_set_ret_val(at_rsp_val_t ret)
{
    rsp_val = ret;
}

typedef int (*at_data_check_cb_t)(char data);

static int atcmd_num_check(char data)
{
    if ((data > '9' || data < '0') && data != '.') {
        return -1;
    }

    return 0;
}

static int atcmd_info_get(char *buf, uint32_t buflen,
                               at_data_check_cb_t valuecheck)
{
    uint32_t i = 0;

    if (NULL == buf || 0 == buflen) {
        return -1;
    }

    do {
        if (at_read(at_dev_fd,&buf[i], 1) < 1) {
            LOGE(TAG, "atcmd_read failed");
            return -1;
        }

        if (buf[i] == ',') {
            buf[i] = 0;
            break;
        } else if (buf[i] == '\r') {
            LOGD(TAG, "delimiter found\n");
            buf[i] = 0;
            return 1;
        }

        if (i >= buflen) {
            LOGE(TAG, "Too long length of data.reader is %s \r\n", buf);
            return -1;
        }
        
        if (NULL != valuecheck) {
            if (valuecheck(buf[i])) {
                LOGE(TAG, "Invalid string!!!, reader is %s last char %d\r\n",
                     buf, buf[i]);
                return -1;
            }
        }
        i++;
    } while (1);

    return 0;
}

static int read_out_string(char *buffer, int size, const char *postfix)
{
    char single;
    int len = 0;

    /* read out prefix */
    do {
        if (at_read(at_dev_fd, &single, 1) < 0) {
            LOGE(TAG, "at read failed!");
            return -1;
        }

        buffer[len++] = single;
        if (len >= size) {
            LOGE(TAG, "Not found!");
            return -1;
        }

        if (len >= strlen(postfix) &&
            memcmp(postfix, buffer + len - strlen(postfix),
                   strlen(postfix)) == 0) {
            break;
        }
    } while(1);

    return len;
}


static int atcmd_send_wait_ret_val(const char *data, int datalen,
                                  at_module_send_state_t state, int timeout_ms) 
{
    int ret = 0;

    if (!atcmd_send_state_ready()) {
        LOGE(TAG, "Not in ready state\r\n");
        return -1;
    }

    atcmd_set_state(state); 
    
    /*it is no need to send data when wait for connect rsp*/
    //if (WAIT_CONN_RSP != state) {
        ret = at_send_no_reply(at_dev_fd, data, datalen, false);
        if (ret < 0) {
            atcmd_set_state(WAIT_NONE);
            LOGE(TAG, "at send failed");
            return -1;
        }
    //}
    
    if (timeout_ms > 0) { 
        if (0 == atcmd_wait_notify(timeout_ms)) {
            ret = atcmd_get_ret_val();
        } else {
            ret = -1;
        }
    }
    
    atcmd_set_state(WAIT_NONE);

    return ret;
}

static void at_rsp_process(at_rsp_val_t rsp_val)
{
    if (atcmd_get_state() != WAIT_SIMPLE_RSP &&
        atcmd_get_state() != WAIT_SEND_PMT) {
        LOGD(TAG, "%s state %d val %d", __func__, atcmd_get_state(), rsp_val);
        return;
    }

    atcmd_set_ret_val(rsp_val);

    atcmd_notify_sender();
}

static void net_pin_check_process(void)
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0;

    /* read out prefix */
    len = read_out_string(prefix, AT_CMD_PREFIX_MAX_LEN, "OK\r\n");
    if (len < 0) {
        return;
    }
    
    if (atcmd_get_state() != WAIT_PIN_RSP) {
        return;
    }

    if (strstr(prefix, "READY") != NULL) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }

    atcmd_notify_sender();
}

static void get_module_imei_process(void)
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0;
    
    if (atcmd_get_state() != WAIT_CGSN_RSP) {
        return;
    }
    
    /* read out prefix */
    len = read_out_string(prefix, AT_CMD_PREFIX_MAX_LEN, "OK\r\n");
    if (len < 0) {
        atcmd_set_ret_val(RSP_ERROR);
        return;
    }
    
    /*try to get imei from prefix*/
    
    atcmd_set_ret_val(RSP_OK);
    atcmd_notify_sender();
}

static void net_sig_quality_process(void)
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0, ret = -1;
    int rssi, ber;
    char single;

    /* eat space */
    at_read(at_dev_fd, &single, 1);

    /* rssi */
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0 || (rssi = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid rssi %s", __func__, __LINE__, prefix);
        return;
    }

    /* ber */
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 1 || (ber = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid ber %s", __func__, __LINE__, prefix);
        return;
    }

    len = read_out_string(prefix, sizeof(prefix), "OK\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }
    
    if (atcmd_get_state() != WAIT_SIGQ_RSP) {
        return;
    }

    LOGI(TAG, "Sig quality %d %d", rssi, ber);

    if (rssi > 11 && rssi < 32) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }

    atcmd_notify_sender();
}

/*AT+CEREG?
+CEREG: 0,1
*/
static void net_register_process()
{
    char prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int  len, ret = -1;
    int  n = 0, stat = 0;

    char single;

    /* eat space */
    at_read(at_dev_fd, &single, 1);

    /* n */
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0 || (n = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid n %s", __func__, __LINE__, prefix);
        return;
    }

    /* stat */
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 1 || (stat = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid stat %s", __func__, __LINE__, prefix);
        return;
    }

    len = read_out_string(prefix, sizeof(prefix), "OK\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }

    if (atcmd_get_state() != WAIT_REG_RSP) {
        return;
    }

    LOGI(TAG, "Register state %d %d", n, stat);

    if ((1 == stat || 5 == stat)) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }

    atcmd_notify_sender();
}

static void net_attach_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len, ret = -1;
    int attach = 0;
    char single;

    /* eat space */
    at_read(at_dev_fd, &single, 1);

    /* attach */
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 1 || (attach = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid attach %s", __func__, __LINE__, prefix);
        return;
    }
    
    len = read_out_string(prefix, sizeof(prefix), "OK\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }
    
    if (atcmd_get_state() != WAIT_ATTCH_RSP) {
        return;
    }

    LOGD(TAG, "Attach state %d", attach);

    if (1 == attach) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }

    atcmd_notify_sender();
}

static void network_error_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int ret = -1;
    int error = 0;
    char single;

    /* eat space */
    at_read(at_dev_fd, &single, 1);

    /* error */
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 1 || (error = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid error %s", __func__, __LINE__, prefix);
        return;
    }
    
    if (atcmd_get_state() == WAIT_NONE) {
        return;
    }

    LOGE(TAG, "CME Error: %d, current state %d", error, atcmd_get_state());

    atcmd_set_ret_val(RSP_ERROR);

    atcmd_notify_sender();
}

static void at_net_cmd_process(void)
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0;

    /* read out prefix */
    len = read_out_string(prefix, AT_CMD_PREFIX_MAX_LEN, ":");
    if (len < 0) {
        return;
    }

    prefix[len - 1] = '\0';
    
    if (memcmp(prefix, "GSN", strlen(prefix)) == 0) {
        get_module_imei_process();
    } else if (memcmp(prefix, "PIN", strlen(prefix)) == 0) {
        net_pin_check_process();
    } else if (memcmp(prefix, "SQ", strlen(prefix)) == 0) {
        net_sig_quality_process();
    } else if (memcmp(prefix, "REG", strlen(prefix)) == 0) {
        net_register_process();
    } else if (memcmp(prefix, "GATT", strlen(prefix)) == 0) {
        net_attach_process(); 
    } else if (memcmp(prefix, "ME ERROR", strlen(prefix)) == 0) {
        network_error_process();
    } else {
        LOGE(TAG, "Unknown preifx %s!", prefix);
    }
} 

/*+XIIC:    0,0.0.0.0
  +XIIC:    1,10.107.216.162*/
static void at_n720_ip_query_process()
{
    char prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0, ret = -1;
    int context_id = 0;

    /*context id check*/
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0) {
        LOGE(TAG, "%s %d invalid error %s", __func__, __LINE__, prefix);
        return;
    }
    context_id = atoi(prefix);
    /*get ip address*/
    memset(prefix, 0, sizeof(prefix));
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 1) {
        LOGE(TAG, "%s %d invalid error %s", __func__, __LINE__, prefix);
        return;
    }

    LOGI(TAG, "n720 module cid %d ip address is %s", context_id, prefix);
    len = read_out_string(prefix, sizeof(prefix), AT_RECV_SUCCESS_POSTFIX);
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }
    
    if (atcmd_get_state() != WAIT_IP_QUERY_RSP) {
        return;
    }

    atcmd_set_ret_val(RSP_OK);

    atcmd_notify_sender();
}

static void at_n720_tcp_setup_rsp_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0, ret = -1;
    int linkid;
    char single;

    /* eat space */
    at_read(at_dev_fd, &single, 1);

    /* linkid */
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0 || (linkid = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid linkid %s", __func__, __LINE__, prefix);
        return;
    }

    memset(prefix, 0, AT_CMD_PREFIX_MAX_LEN);
    /* get result */
    len = read_out_string(prefix, sizeof(prefix), "\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }
    
    if (atcmd_get_state() != WAIT_CONN_RSP) {
        return;
    }

    LOGI(TAG, "linkid %d connect %s", linkid, prefix);

    if (memcmp(prefix, "OK", strlen("OK")) == 0) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }

    atcmd_notify_sender();
}

static void at_n720_tcp_close_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int linkid, len;
    
    /* get result */
    len = read_out_string(prefix, sizeof(prefix), "\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }
    
    LOGI(TAG, "linkid close result %s", prefix);

    if (strstr(prefix, "Closed") != NULL) {
        /*then close connect*/
        prefix[1] = 0;
        linkid = atoi(prefix);
        LOGI(TAG, "Link %d is closed by remote ", linkid);
        aos_mutex_lock(&g_link_mutex, AOS_WAIT_FOREVER);
        g_link[linkid].fd = -1;
        aos_mutex_unlock(&g_link_mutex);
        return ;
    }

    if (atcmd_get_state() != WAIT_DISC_RSP) {
        return;
    }
    
    if (strstr(prefix, "OK") != NULL) {
        atcmd_set_ret_val(RSP_OK);
    } else {
        atcmd_set_ret_val(RSP_ERROR);
    }
    
    atcmd_notify_sender();
}

static void at_n720_tcp_send_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0;
    
    /* get result */
    len = read_out_string(prefix, sizeof(prefix), "\r\n");
    if (len < 0) {
        LOGE(TAG, "Fail to get expect response");
        return;
    }

    LOGI(TAG, "linkid send result %s", prefix);
    
    if (atcmd_get_state() != WAIT_SEND_RSP) {
        return;
    }

    if (strstr(prefix, "EXPIRED") != NULL ||
        strstr(prefix, "FAILED") != NULL ||
        strstr(prefix, "ERROR") != NULL) {
        atcmd_set_ret_val(RSP_ERROR);
    } else {
        atcmd_set_ret_val(RSP_OK);
    }

    atcmd_notify_sender();
}

/*+TCPRECV: <n>,<length>,data\r\n*/
static void at_n720_tcp_recv_data_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    char  remote_ip[16] = {0};
    char  *data = NULL;
    uint16_t remote_port = 0;
    int len = 0;
    int ret = -1;
    int linkid;

    /* eat space */
    at_read(at_dev_fd, prefix, 1);

    memset(prefix, 0, AT_CMD_PREFIX_MAX_LEN);
    /*Get linkid */
    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0 || (linkid = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid linkid %s", __func__, __LINE__, prefix);
        return;
    }

    ret = atcmd_info_get(prefix, sizeof(prefix), atcmd_num_check);
    if (ret != 0 || (len = atoi(prefix)) < 0) {
        LOGE(TAG, "%s %d invalid len %s", __func__, __LINE__, prefix);
        return;
    }

    data = aos_malloc(len);
    if (NULL == data) {
        LOGE(TAG, "Fail to malloc %d ", len);
        return;
    }
    memset(data, 0, len);

    /* get data */
    if (at_read(at_dev_fd, data, len) < 0) {
        LOGE(TAG, "Fail to get data %d ", len);
        return;
    }

    if (g_netconn_data_input_cb && (g_link[linkid].fd >= 0)) {
        if (g_netconn_data_input_cb(g_link[linkid].fd, data, len, remote_ip, remote_port)) {
            LOGE(TAG, " %s socket %d get data len %d fail to post to sal, drop it\n",
                 __func__, g_link[linkid].fd, len);
        }
    }
    
    LOGI(TAG, "%s socket data on link %d with length %d posted to sal\n",
         __func__, linkid, len);
    
    aos_free(data);
    return;
}

static void at_n720_module_tcp_rsp_process()
{
    char  prefix[AT_CMD_PREFIX_MAX_LEN] = { 0 };
    int len = 0;

    /* read out prefix */
    len = read_out_string(prefix, AT_CMD_PREFIX_MAX_LEN, ":");
    if (len < 0) {
        return;
    }

    prefix[len - 1] = '\0';

    if (memcmp(prefix, "SETUP", strlen(prefix)) == 0) {
        at_n720_tcp_setup_rsp_process();
    } else if (memcmp(prefix, "CLOSE", strlen(prefix)) == 0) {
        at_n720_tcp_close_process();
    } else if (memcmp(prefix, "SEND", strlen(prefix)) == 0) {
        at_n720_tcp_send_process();
    } else if (memcmp(prefix, "RECV", strlen(prefix)) == 0) {
        at_n720_tcp_recv_data_process();
    } else {
        LOGE(TAG, "Unknown preifx %s!", prefix);
    }
}

static void at_n720_module_rsp_callback(void *arg, char *rspinfo, int rsplen)
{
    char *input = (char *)arg;

    if (NULL == input) {
        LOGE(TAG, "invalid input of rsp callback");
        return;
    }

    if (0 == memcmp(input, AT_RECV_SUCCESS_POSTFIX,
                    strlen(AT_RECV_SUCCESS_POSTFIX))) {
        at_rsp_process(RSP_OK);
    } else if (0 == memcmp(input, AT_RECV_FAIL_POSTFIX,
                    strlen(AT_RECV_FAIL_POSTFIX))) {
        at_rsp_process(RSP_ERROR);
    } else if (0 == memcmp(input, AT_CMD_NET_CMD_PREFIX,
                    strlen(AT_CMD_NET_CMD_PREFIX))) {
        at_net_cmd_process();
    } else if (0 == memcmp(input, AT_CMD_XIIC_PREFIX,
                    strlen(AT_CMD_XIIC_PREFIX))) {
        at_n720_ip_query_process();
    } else if (0 == memcmp(input, AT_CMD_TCP_PREFIX,
                    strlen(AT_CMD_TCP_PREFIX))) {
        at_n720_module_tcp_rsp_process();
    } else if (0 == memcmp(input, AT_CMD_TCP_SEND_PROMPT,
                    strlen(AT_CMD_TCP_SEND_PROMPT))){
        at_rsp_process(RSP_PROMPT);
    } else {
        LOGE(TAG, "invalid input of rsp callback");
        return;
    }

    return;
}

static int at_n720_module_ready(void)
{
    return atcmd_send_wait_ret_val(AT_CMD_AT, strlen(AT_CMD_AT),
                               WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT); 
}

static int at_n720_echo_off(void)
{      
    return atcmd_send_wait_ret_val(AT_CMD_ECHO_OFF, strlen(AT_CMD_ECHO_OFF),
                                   WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT); 
}

static int at_n720_network_status_check()
{
    int retry = 0;
    int ret   = 0;

    do {
        retry++;
        /*module IMEI check*/
        ret = atcmd_send_wait_ret_val(AT_CMD_IMEI_GET, strlen(AT_CMD_IMEI_GET),
                                  WAIT_CGSN_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 3);
    if (ret) {
        LOGD(TAG, "Fail to get module IMEI and continue to run");
    }
    
    retry = 0;
    do {
        retry++;
        /*simcard pin check*/
        ret = atcmd_send_wait_ret_val(AT_CMD_SIM_PIN_CHECK, strlen(AT_CMD_SIM_PIN_CHECK),
                                      WAIT_PIN_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 15);
    
    if (ret) {
        LOGE(TAG, "simcard check fail reinit module");
        return -1;
    }
    
    /*try to get imsi twice at most , and if error, nothing to do*/
    retry = 0;
    do {
        retry++;
        /* Signal quaility check */
        ret = atcmd_send_wait_ret_val(AT_CMD_SIGNAL_QUALITY_CHECK, strlen(AT_CMD_SIGNAL_QUALITY_CHECK),
                                      WAIT_SIGQ_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 40);
        
    if (ret) {
        LOGE(TAG, "Signal quaility check %d times failed\r\n", retry);
        return -1;
    }

    return 0;

}

static int at_n720_network_reg_and_check()
{
    int ret = 0;
    int retry = 0;
    
#if 0
    char command[32] = {0};
    /* network registration set*/
    snprintf(command, 32, "AT+CREG=%d\r", 2);
    ret = atcmd_send_wait_ret_val(command, strlen(command),
                                  WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
    if (ret) {
        LOGE(TAG, " %s fail, continue ", command);
    }
#endif
    do {
        retry++;
        /* network registration check*/
        ret = atcmd_send_wait_ret_val(AT_CMD_NETWORK_REG_CHECK, strlen(AT_CMD_NETWORK_REG_CHECK),
                                      WAIT_REG_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 40);
    
    if (ret != 0) {
        LOGE(TAG, "Network registration check failed\r\n");
        return -1;
    }
    
    retry = 0;
    do {
        retry++;
        /* PS attach check*/
        ret = atcmd_send_wait_ret_val(AT_CMD_ATTACH_CHECK, strlen(AT_CMD_ATTACH_CHECK),
                                      WAIT_ATTCH_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 30);
    
    if (ret != 0) {
        LOGE(TAG, "Attchement check failed\r\n");
        return -1;
    }
    
    return 0;
}

static int at_n720_pdp_config(void)
{
    int ret = 0;
    int retry = 0;
    int contextid = 1;
    int auth_type = 0;     /*none*/
    char command[32] = {0};

#define CONTEXT_TYPTE "\"IP\""
#define APN_TYPE    "\"CMNET\""
#define APN_USERNAME "\"\""
#define APN_PASSWORKD "\"\""

    memset(command, 0, sizeof(command));
    snprintf(command,
             sizeof(command),
             AT_CMD_PDP_SET, contextid,
             CONTEXT_TYPTE, APN_TYPE);
    do {
        retry++;
        ret = atcmd_send_wait_ret_val(command, strlen(command),
                                         WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 3);
    
    if (0 != ret) {
        LOGE(TAG, "%s cmd fail", command);
        return -1;
    }
                                         
    memset(command, 0, sizeof(command));
    snprintf(command,
                sizeof(command),
                AT_CMD_PDP_AUTH, contextid, auth_type, APN_USERNAME, APN_PASSWORKD);
    retry = 0;
    do {
        retry++;
        ret = atcmd_send_wait_ret_val(command, strlen(command),
                                         WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 3);
    
    
    retry = 0;
    do {
        retry++;
        ret = atcmd_send_wait_ret_val(AT_CMD_PDP_CONNECT, strlen(AT_CMD_PDP_CONNECT),
                                         WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
        aos_msleep(1000);
    } while (ret != 0 && retry < 3);
    
    if (0 != ret) {
        LOGE(TAG, "%s cmd fail", AT_CMD_PDP_CONNECT);
        return -1;
    }

    return 0;
}

static int at_n720_set_recvmode(at_recvmode_e recvmode)
{
    int ret = 0;
    char command[32] = {0};

    memset(command, 0, 32);
    
    snprintf(command, sizeof(command), "AT+RECVMODE=%d\r", recvmode);
    ret = atcmd_send_wait_ret_val(command, strlen(command),
                                 WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
    return ret;
}



static int at_n720_get_ip()
{
    int ret = 0;
    ret = atcmd_send_wait_ret_val(AT_CMD_PDP_IP_QUERY, strlen(AT_CMD_PDP_IP_QUERY),
                                     WAIT_IP_QUERY_RSP, AT_WAIT_DEF_TIMEOUT);
    return ret;
}

static int at_n720_module_init()
{
    int ret = 0;
    int retry = 0;

    do {
        retry++;
        ret = at_n720_module_ready();
        aos_msleep(1000);
    } while (ret != 0 && retry < 15);
    if (ret) { 
        LOGE(TAG, "module n720 haven't ready yet");
        return -1;
    }
    
    retry = 0;
    do {
        retry++;
        ret = at_n720_echo_off();
        aos_msleep(1000);
    } while (ret != 0 && retry < 3);
    if (ret) {
        LOGE(TAG, "module n720 echo off fail");
    }

    ret = at_n720_network_status_check();
    if (ret) {
        LOGE(TAG, "at_n720_network_status_check fail ");
        return -1;
    }

    ret = at_n720_network_reg_and_check();
    if (ret) {
        LOGE(TAG, "at_n720_network_reg_and_check fail ");
        return -1;
    }

    ret = at_n720_pdp_config();
    if (ret) {
        LOGE(TAG, "at_n720_pdp_config fail ");
        return -1;
    }

    return 0;
}

static int fd_to_linkid(int fd)
{
    int link_id;

    if (aos_mutex_lock(&g_link_mutex, AOS_WAIT_FOREVER) != 0) {
        LOGE(TAG, "Failed to lock mutex (%s).", __func__);
        return -1;
    }
    for (link_id = 0; link_id < N720_MAX_LINK_NUM; link_id++) {
        if (g_link[link_id].fd == fd) {
            break;
        }
    }

    aos_mutex_unlock(&g_link_mutex);

    return link_id;
}

static int HAL_SAL_Init(void)
{
    uint32_t linknum = 0;

    if (inited) {
        LOGI(TAG, "n720 module have already inited \r\n");
        return 0;
    }

    if (0 != aos_mutex_new(&g_link_mutex)) {
        LOGE(TAG, "Creating link mutex failed (%s %d).", __func__, __LINE__);
        goto err;
    }

    if (0 != aos_sem_new(&g_st_at_sem, 0)) {
        LOGE(TAG, "Creating module at sem4 failed (%s %d).", __func__, __LINE__);
        goto err;
    }
    
    memset(g_link, 0, sizeof(g_link));

    for (linknum = 0; linknum < N720_MAX_LINK_NUM; linknum++) {
        g_link[linknum].fd = -1;
    }
    
    if (at_n720_module_init()) {
        LOGE(TAG,"module init fail");
        goto err;
    }

    if (at_n720_get_ip() == 0) {
        aos_post_event(EV_WIFI, CODE_WIFI_ON_GOT_IP, 0xdeaddead);
    }
    
    inited = 1;

    return 0;
err:

    if (aos_mutex_is_valid(&g_link_mutex)) {
        aos_mutex_free(&g_link_mutex);
    }

    if (aos_sem_is_valid(&g_st_at_sem)) {
        aos_sem_free(&g_st_at_sem);
    }
    
    inited = 0;
    return -1;
}

static int HAL_SAL_Deinit()
{
    if (!inited) {
        return 0;
    }

    if (aos_mutex_is_valid(&g_link_mutex)) {
        aos_mutex_free(&g_link_mutex);
    }
    
    if (aos_sem_is_valid(&g_st_at_sem)) {
        aos_sem_free(&g_st_at_sem);
    }
    inited = 0;

    return 0;
}

static int HAL_SAL_DomainToIp(char *domain, char ip[16])
{
    return -1;
}

static int HAL_SAL_Start(sal_conn_t *conn)
{
    int  linkid = 0;
    int  ret = 0;
    char *pccmd = NULL;

    if (!inited) {
        LOGE(TAG, "%s m02h lte module haven't init yet \r\n", __func__);
        return -1;
    }

    if (!conn || !conn->addr) {
        LOGE(TAG, "%s %d - invalid input \r\n", __func__, __LINE__);
        return -1;
    }

    aos_mutex_lock(&g_link_mutex, AOS_WAIT_FOREVER);
    for (linkid = 0; linkid < N720_MAX_LINK_NUM; linkid++) {
        if (g_link[linkid].fd >= 0) {
            continue;
        }
        g_link[linkid].fd = conn->fd;
        break;
    }
    aos_mutex_unlock(&g_link_mutex);

    if (linkid >= N720_MAX_LINK_NUM) {
        LOGE(TAG, "No link available for now, %s failed. \r\n", __func__);
        return -1;
    }

    pccmd = aos_malloc(N720_MAX_AT_CMD_LENGTH);
    if (NULL == pccmd) {
        LOGE(TAG, "fail to malloc %d at %s \r\n", N720_MAX_AT_CMD_LENGTH, __func__);
        goto err;
    }
    memset(pccmd, 0, N720_MAX_AT_CMD_LENGTH);

    switch (conn->type) {
        case TCP_SERVER:
            LOGE(TAG, "tcp support not supported for now on n720");
            goto err;
            break;
        case TCP_CLIENT:
            snprintf(pccmd, N720_MAX_AT_CMD_LENGTH - 1, "%s=%d\r", AT_CMD_TCP_CLOSE, linkid);
            ret = atcmd_send_wait_ret_val(pccmd, strlen(pccmd), WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
            if (ret != 0) {
                LOGE(TAG, "%s failed \r\n", pccmd);
            }
            memset(pccmd, 0, N720_MAX_AT_CMD_LENGTH);
            
            snprintf(pccmd, N720_MAX_AT_CMD_LENGTH - 1, "%s=%d,%s,%d\r", AT_CMD_TCP_CONNECT,
                    linkid, conn->addr, conn->r_port);
            /*wait for set up at command reponse*/
            #if 0
            ret = atcmd_send_wait_ret_val(pccmd, strlen(pccmd), WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
            if (ret != 0) {
                LOGE(TAG, "%s failed \r\n", pccmd);
                goto err;
            }
            #endif
            /*wait for tcp connect reponse*/
            ret = atcmd_send_wait_ret_val(pccmd, strlen(pccmd), WAIT_CONN_RSP, AT_WAIT_DEF_TIMEOUT * 30);
            if (ret != 0) {
                LOGE(TAG, "tcp connect %s response failed \r\n", pccmd);
                goto err;
            }
            break;
        case UDP_UNICAST:
        case SSL_CLIENT:
        case UDP_BROADCAST:
        default:
            LOGE(TAG, "n720 lte module connect type %d not support \r\n", conn->type);
            goto err;
    }

    aos_free(pccmd);
    return 0;
err:
    aos_free(pccmd);
    aos_mutex_lock(&g_link_mutex, AOS_WAIT_FOREVER);
    g_link[linkid].fd = -1;
    aos_mutex_unlock(&g_link_mutex);
    return -1;
}

static int HAL_SAL_Close(int fd, int32_t remote_port)
{
    int  linkid = 0;
    int  ret = 0;
    char cmd[32] = {0};

    if (!inited) {
        LOGE(TAG, "%s n720 lte module haven't init yet \r\n", __func__);
        return -1;
    }

    linkid = fd_to_linkid(fd);
    if (linkid >= N720_MAX_LINK_NUM) {
        LOGE(TAG, "No connection found for fd (%d) in %s \r\n", fd, __func__);
        return -1;
    }

    snprintf(cmd, 32 - 1, "%s=%d\r", AT_CMD_TCP_CLOSE, linkid);
    ret = atcmd_send_wait_ret_val(cmd, strlen(cmd), WAIT_SIMPLE_RSP, AT_WAIT_DEF_TIMEOUT);
    if (ret != 0) {
        LOGE(TAG, "%s failed \r\n", cmd);
    }

    aos_mutex_lock(&g_link_mutex, AOS_WAIT_FOREVER);
    g_link[linkid].fd = -1;
    aos_mutex_unlock(&g_link_mutex);

    return ret;
}


static int HAL_SAL_Send(int fd,
                 uint8_t *data,
                 uint32_t len,
                 char remote_ip[16],
                 int32_t remote_port,
                 int32_t timeout)
{
    int  linkid;
    int  link_send_capacity = 4096;
    int  ret = 0;
    char cmd[32] = {0};

    if (!inited) {
        LOGE(TAG, "%s n720 lte module haven't init yet \r\n", __func__);
        return -1;
    }
    
    linkid = fd_to_linkid(fd);
    if (linkid >= N720_MAX_LINK_NUM) {
        LOGE(TAG, "No connection found for fd (%d) in %s \r\n", fd, __func__);
        return -1;
    }

    /*get the datalength can be sent at this time*/
    if (link_send_capacity < len) {
        LOGE(TAG, "Data can be send on link %d oversize len %d capacity %d", 
            linkid, len, link_send_capacity);
        return -1;
    }
    
    /*try start tcp length*/
    snprintf(cmd, 32 - 1, "%s=%d,%d\r", AT_CMD_TCP_SEND, linkid, len);
    ret = atcmd_send_wait_ret_val(cmd, strlen(cmd), WAIT_SEND_PMT, AT_WAIT_DEF_TIMEOUT);
    if (ret != RSP_PROMPT) {
        LOGE(TAG, "%s failed \r\n", cmd);
        return -1;
    }

    /*get prompt then sleep 50ms send tcp data*/
    aos_msleep(50);

    /*send tcp data*/
    ret = atcmd_send_wait_ret_val((char *)data, len, WAIT_SEND_RSP, AT_WAIT_DEF_TIMEOUT);
    if (ret != 0) {
        LOGE(TAG, "tcp %d send data failed \r\n", linkid);
        return -1;
    }

    return 0;
}

static int HAL_SAL_RegisterNetconnDataInputCb(netconn_data_input_cb_t cb)
{
    if (cb) {
        g_netconn_data_input_cb = cb;
    }
    return 0;
}

static int n720_sal_add_dev(void* data)
{
    at_config_t at_config = { 0 };

    at_init();

    if(data != NULL)
    {
        sal_device_config_t* config = (sal_device_config_t *)data;
        uart_dev.port  = config->uart_dev.port;
        uart_dev.config.baud_rate    = config->uart_dev.config.baud_rate;
        uart_dev.config.data_width   = config->uart_dev.config.data_width;
        uart_dev.config.parity       = config->uart_dev.config.parity;
        uart_dev.config.stop_bits    = config->uart_dev.config.stop_bits;
        uart_dev.config.flow_control = config->uart_dev.config.flow_control;
        uart_dev.config.mode         = config->uart_dev.config.mode;
    }
    else
    {
        LOGE(TAG, "Error: Uart dev is not configured! Please"
             " provide the config in sal_add_dev() call.\n");
        return -1;
    }

    /* configure and add one uart dev */
    at_config.type                             = AT_DEV_UART;
    at_config.port                             = uart_dev.port;
    at_config.dev_cfg                          = &uart_dev;
    at_config.send_wait_prompt                 = AT_SEND_DATA_WAIT_PROMPT;

    if ((at_dev_fd = at_add_dev(&at_config)) < 0) {
        LOGE(TAG, "AT parser device add failed!\n");
        return -1;
    }

    /*AT command process success*/
    at_register_callback(at_dev_fd, AT_RECV_SUCCESS_POSTFIX,
                     NULL, NULL, 0,
                     at_n720_module_rsp_callback,
                     AT_RECV_SUCCESS_POSTFIX);
    
    /*AT command process fail*/
    at_register_callback(at_dev_fd, AT_RECV_SUCCESS_POSTFIX,
                         NULL, NULL, 0,
                         at_n720_module_rsp_callback,
                         AT_RECV_FAIL_POSTFIX);
    /*AT command network */
    at_register_callback(at_dev_fd, AT_CMD_NET_CMD_PREFIX, 
                        NULL, NULL, 0, 
                        at_n720_module_rsp_callback, 
                        AT_CMD_NET_CMD_PREFIX);
    
    at_register_callback(at_dev_fd, AT_CMD_XIIC_PREFIX,
                         NULL, NULL, 0,
                         at_n720_module_rsp_callback,
                         AT_CMD_XIIC_PREFIX);

    at_register_callback(at_dev_fd, AT_CMD_TCP_PREFIX,
                         NULL, NULL, 0,
                         at_n720_module_rsp_callback,
                         AT_CMD_TCP_PREFIX);

    at_register_callback(at_dev_fd, AT_CMD_TCP_SEND_PROMPT,
                         NULL, NULL, 0,
                         at_n720_module_rsp_callback,
                         AT_CMD_TCP_SEND_PROMPT);
    
    return 0;
}

static sal_op_t sal_op = {
    .next = NULL,
    .version = "1.0.0",
    .name = "n720",
    .add_dev = n720_sal_add_dev,
    .init = HAL_SAL_Init,
    .start = HAL_SAL_Start,
    .send_data = HAL_SAL_Send,
    .domain_to_ip = HAL_SAL_DomainToIp,
    .finish = HAL_SAL_Close,
    .deinit = HAL_SAL_Deinit,
    .register_netconn_data_input_cb = HAL_SAL_RegisterNetconnDataInputCb,
};

/* Generated by aos tools, do NOT modify! */
#ifndef __SAL_DEV_NAME
#error "__SAL_DEV_NAME C mocro not defined, please define it in the SAL device driver."
#endif

int __SAL_DEV_NAME_sal_device_init(void)
{
    return sal_module_register(&sal_op);
}
